// 网络请求相关日志中间件

import { Response, NextFunction, Request } from 'express'
import lodash from 'lodash'
import { IException } from '@/types/common'
import { catchErrorHandler, logDataByRequest } from '@/utils'
import { bindRequest, bindResponse, clearRequestScope, getContainer } from '@/container/utils'
import logger, { consoleLogger } from '@/utils/logger'

// 格式化响应体，如果是json字符串则转为json对象，否则原样返回
const formatResponseBody = (body: any) => {
  if (lodash.isString(body)) {
    try {
      const bodyJson = JSON.parse(body)
      if (typeof bodyJson === 'object') {
        return bodyJson
      }
    } catch (error) {
      return body
    }
  }
  return body
}

// 请求中间件，绑定请求对象到容器
// 请求完成日志处理中间件, 记录请求日志
// 这个要在所有路由注入之前，不然不会执行之后的路由
export const requestMiddleware = (request: Request, response: Response, next: NextFunction) => {
  const container = getContainer()
  container.middleware(() => {
    // 绑定请求和响应对象到容器
    bindRequest(request)
    bindResponse(response)
    // 绑定请求和响应对象到容器
    // 重写 res.send 方法，获取返回数据
    const originalSend = response.send
    response.send = function (body: any) {
      // 如果请求已完成完成，则拦截，不执行send方法
      if (request.isFinished) {
        return this.end(() => {
          logger.error('请求完成后重复返回，已被终止')
          logger.error('响应数据：', body)
        })
      }
      // 暂存返回数据
      response.locals.cacheResBody = formatResponseBody(body)
      return originalSend.call(this, body)
    }
    // 记录请求开始时间
    const requestTime = Date.now()
    request.requestTime = requestTime
    response.on('finish', async () => {
      // 标记请求已完成
      request.isFinished = true
      await logDataByRequest(request, response)
      // 清空请求生命周期内的服务实例
      clearRequestScope()
    })
    next()
  })
}

// 捕获异常日志处理中间件，记录异常日志并返回异常信息
// 这个要放在所有路由注入之后 写入，不然在他之后的路由都不生效
export const errorRequestMiddleware = (error: IException, req: Request, res: Response, _next: NextFunction) => {
  // 记录异常日志
  consoleLogger.error(error)
  logger.error(error)
  // 异常信息绑定到响应对象
  req.errorInfo = error
  // 处理异常反应
  catchErrorHandler(error, res)
}

// 生成路由函数专用的处理中间件
// 因为路由函数主要用于处理请求，并不会把一些不必要的异常捕获
// 所以这里专门处理路由函数的异常捕获，并且传递给全局异常处理中间件
export const routeHandlerMiddleware = (
  handler: (req: Request, res: Response, next: NextFunction) => void | Promise<void>
) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      await handler(req, res, next)
    } catch (error) {
      next(error)
    }
  }
}
